Now that you have constructed and consumed a single-file assembly, let’s examine the process of building a multifile assembly. Recall that a multifile assembly is simply a collection of related modules that is deployed and versioned as a single logical unit. At the time of this writing, the Visual Studio IDE does not support a C# multifile assembly project template. Therefore, you will need to make use of the command-line compiler (csc.exe) to build such a beast.
To describe the process, you will build a multifile assembly named AirVehicles. The primary module (airvehicles.dll) will contain a single class type named Helicopter. The related manifest (also contained in airvehicles.dll) will catalog an additional *.netmodule file named ufo.netmodule that contains another class type named (of course) Ufo. Although both class types are physically contained in separate binaries, you will group them into a single namespace named AirVehicles. You’ll create both classes using C# (although you could certainly mix and match languages if you desire).
To begin, open a simple text editor (such as Notepad) and create the following Ufo class definition and save it to a file named ufo.cs:
using System; namespace AirVehicles { public class Ufo { public void AbductHuman() { Console.WriteLine("Resistance is futile"); } } }
To compile this class into a .NET module, open a Visual Studio 2010 Command Prompt, navigate to the folder containing ufo.cs and issue the following command to the C# compiler (the module option of the /target flag instructs csc.exe to produce a *.netmodule as opposed to a *.dll or an *.exe file):
csc.exe /t:module ufo.cs
If you now look in the folder that contains the ufo.cs file, you should see a new file named ufo.netmodule (take a peek in your working directory). Next, create a new file named helicopter.cs that contains the following class definition:
using System; namespace AirVehicles { public class Helicopter { public void TakeOff() { Console.WriteLine("Helicopter taking off!"); } } }
Given that airvehicles.dll is the intended name of the primary module of this multifile assembly, you will need to compile helicopter.cs using the /t:library and /out: options. To enlist the ufo.netmodule binary into the assembly manifest, you must also specify the /addmodule flag. The following command does the trick:
csc /t:library /addmodule:ufo.netmodule /out:airvehicles.dll helicopter.cs
At this point, your directory should contain the primary airvehicles.dll module as well as the secondary ufo.netmodule binaries.
Now, using ildasm.exe, open ufo.netmodule. As you can see, *.netmodules contain a module-level manifest; however, its sole purpose is to list each external assembly referenced by the code base. Given that the Ufo class did little more than make a call to Console.WriteLine(), you find the following core information:
.assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) .ver 4:0:0:0 } .module ufo.netmodule
Next, using ildasm.exe, open the primary airvehicles.dll module and investigate the assembly-level manifest. Notice that the .file token documents the associated modules in the multifile assembly (ufo.netmodule in this case). The .class extern tokens are used to document the names of the external types referenced for use from the secondary module (Ufo). Here is the relevant information:
.assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) .ver 4:0:0:0 } .assembly airvehicles { ... .hash algorithm 0x00008004 .ver 0:0:0:0 } .file ufo.netmodule ... .class extern public AirVehicles.Ufo { .file ufo.netmodule .class 0x02000002 } .module airvehicles.dll
Again, realize that the only entity that links together airvehicles.dll and ufo.netmodule is the assembly manifest. These two binary files have not been merged into a single, larger *.dll.
The consumers of a multifile assembly couldn’t care less that the assembly they are referencing is composed of numerous modules. To keep things simple, let’s create a new C# client application at the command line. Create a new file named Client.cs with the following module definition. When you are done, save it in the same location as your multifile assembly.
using AirVehicles; class Program { static void Main() { Console.WriteLine("***** Multifile Assembly Client *****"); Helicopter h = new Helicopter(); h.TakeOff(); // This will load the *.netmodule on demand. Ufo u = new Ufo(); u.AbductHuman(); Console.ReadLine(); } }
To compile this executable assembly at the command line, use the C# command-line compiler, csc.exe, with the following command set:
csc /r:airvehicles.dll Client.cs
Notice that when you are referencing a multifile assembly, the compiler needs to be supplied only with the name of the primary module (the *.netmodules are loaded on demand by the CLR when used by the client’s code base). In and of themselves, *.netmodules do not have an individual version number and can’t be directly loaded by the CLR. Individual *.netmodules can be loaded only by the primary module (e.g., the file that contains the assembly manifest).
Note Visual Studio 2010 also allows you to reference a multifile assembly. Simply use the Add References dialog box and select the primary module. Any related *.netmodules are copied during the process.
At this point, you should feel comfortable with the process of building both single-file and multifile assemblies. To be completely honest, chances are very good that all of your assemblies will be single-file entities. Nevertheless, multifile assemblies can prove helpful when you wish to break a large physical binary into modular units (which are quite useful for remote download scenarios). Next up, let’s formalize the concept of a private assembly.
Source Code The MultifileAssembly code files are included under the Chapter 14 subdirectory.